#ifndef CDN_SYNC_PACKAGE_DOWNLOAD_H
#define CDN_SYNC_PACKAGE_DOWNLOAD_H

#pragma once

#include "URLDownloadTools.h"

#ifdef BUILDING_SMART_DOWNLOAD
    class DownloadDialog;
    typedef class DownloadDialog PkgDwnldClient;
    typedef class DownloadDialog PkgListClient;
#else
    class CheckUpdatesDialog;
    namespace CasinoClient
    {
        class PatchClient;
    }
    typedef class CasinoClient::PatchClient PkgDwnldClient;
    typedef class CheckUpdatesDialog PkgListClient;
#endif

class PackageManager;


// --------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
//
// Smart Package Download Class
//
// --------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------

class CDNSmartPackageDownload
{
    public:
        CDNSmartPackageDownload(PkgDwnldClient* pkgDownloadClient,
                                const String& cdnAddress, 
                                const String& cdnPackageList,
                                const String& package_name);
        CDNSmartPackageDownload(PkgListClient* pkgListClient,
                                const String& cdnAddress, 
                                const String& cdnPackageList);
    private:
        // common initialization
        void Init(PkgDwnldClient* pkgDownloadClient,
                  PkgListClient* pkgListClient,
                  const String& cdnAddress, 
                  const String& cdnPackageList,
                  const String& package_name);

    public:
        virtual ~CDNSmartPackageDownload();

        // Initiate download for the given package
        bool start();

        // NOTE: Currently it is not possible to throttle the download speed
        uint32 throughput();
        void throughput(uint32 bps);

        // called to signal "stop downloading file(s)"
        void cancel();
        bool isCancelled() const;

        enum DownloadType
        {
            DWNLD_TYPE_PACKAGE,
            DWNLD_TYPE_SYNC
        };

        enum ServerError
        {
            SERR_None,
            SERR_DownForMaint,
            SERR_UnknownPackage,
            SERR_TransferProblem,
            SERR_IOError,
            SERR_LobbyUpdateProblem,
            SERR_Unrecognized
        };

    protected:  // nested class
        class CDNProgressHandler : public URLDownloadProgressEventHandler
        {
        public:
            CDNProgressHandler(CDNSmartPackageDownload* pPackageDownload)
            {
                m_pPackageDownload = pPackageDownload;
            }

        public:
            virtual bool isCancelled() const
            {
                return m_pPackageDownload->isCancelled();
            }
            virtual void UpdateProgress(ULONG ulNewAmountDownloaded   /* new bytes downloaded since last event */, 
                                        ULONG ulTotalAmountDownloaded /* total bytes downloaded thus far */, 
                                        ULONG ulTotalFileSize         /* total bytes in file being downloaded */  ) const
            {
                // TODO
            }

        protected:
            CDNSmartPackageDownload* m_pPackageDownload;
        };

    protected:
        CDNProgressHandler* m_cdnProgressHandler;
        PackageManager*     m_cdnPackageManager;
        PkgDwnldClient*     m_PkgDwnldClient;
        PkgListClient*      m_PkgListClient;
        HANDLE              m_hDownloadThread;
        DownloadType        m_DownloadType;
        bool                m_bPackageListChanged;
        String              m_cdnAddress;
        String              m_PackageListINI;
        String              m_package;
        uint32              m_crcPackage;
        uint32              m_bps;
        LONG                m_bCancelled;
        uint32              m_bytesIn;
        uint32              m_total;
        uint32              m_bytesRead;
        uint32              m_begintime;
        uint32              m_overalltime;
        uint32              m_lasteventtime;
        uint32              m_lasteventbyte;
        uint32              m_lasteventbytes[4];

    public:
        static const String PACKAGELIST_INI;    // "package_list.ini"
};


// --------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
//
// Package Management Classes
//
// --------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------

class PackageManifest
{
public:
    PackageManifest(const String& casino_name, const String& skin_name, const String& casino_release, 
                    const String& package_name, const String& package_version, const String& remote_folder,
                    uint32 file_count, uint32 total_size, const String& created_on)
    {
        m_casino_name = casino_name;
        m_skin_name = skin_name;
        m_casino_release = casino_release;
        m_package_name = package_name;
        m_package_version = package_version;
        m_remote_folder = remote_folder;
        m_file_count = file_count;
        m_total_size = total_size;
        m_created_on = created_on;
    }
    const String& getCasinoName() const
    {
        return m_casino_name;
    }
    const String& getSkinName() const
    {
        return m_skin_name;
    }
    const String& getCasinoRelease() const
    {
        return m_casino_release;
    }
    const String& getPackageName() const
    {
        return m_package_name;
    }
    const String& getPackageVersion() const
    {
        return m_package_version;
    }
    const String& getRemoteFolder() const
    {
        return m_remote_folder;
    }
    uint32 getFileCount() const
    {
        return m_file_count;
    }
    uint32 getTotalSize() const
    {
        return m_total_size;
    }
    const String& getCreatedOn() const
    {
        return m_created_on;
    }

private:
    String  m_casino_name;
    String  m_skin_name;
    String  m_casino_release;
    String  m_package_name;
    String  m_package_version;
    String  m_remote_folder;
    uint32  m_file_count;
    uint32  m_total_size;
    String  m_created_on;
};

class PackageFileEntry
{
public:
    PackageFileEntry()
    {
        m_size = 0;
        m_crc = 0;
    }
    PackageFileEntry(const String& local_file, const String& remote_file, uint32 size, uint32 crc)
    {
        m_local_file = local_file;
        m_remote_file = remote_file;
        m_size = size;
        m_crc = crc;
    }
    const String& getLocalFileName() const
    {
        return m_local_file;
    }
    const String& getRemoteFileName() const
    {
        return m_remote_file;
    }
    uint32 getFileSize() const
    {
        // size of the local, uncompressed file
        return m_size;
    }
    uint32 getFileCRC() const
    {
        // CRC of the local, uncompressed file
        return m_crc;
    }

private:
    String  m_local_file;
    String  m_remote_file;
    uint32  m_size; // size of the local, uncompressed file
    uint32  m_crc;  // CRC of the local, uncompressed file
};

class PackageManager
{
public:
    PackageManager(const String& name)
    {
        m_name = name;
        m_manifest = NULL;
        m_filelist = NULL;
    }
   ~PackageManager()
    {
        delete [] m_filelist;
        delete m_manifest;
    }

    bool isLoaded()
    {
        return m_manifest != NULL  &&  m_filelist != NULL;
    }
    bool LoadPackage(const String& filename)
    {
        return false;
    }
    uint32 getFileCount() const
    {
        return m_manifest  ?  m_manifest->getFileCount() : 0;
    }
    uint32 getTotalSize() const
    {
        return m_manifest  ?  m_manifest->getTotalSize() : 0;
    }
    const PackageManifest* getManifest() const
    {
        return m_manifest;
    }
    const PackageFileEntry* getFileList() const
    {
        return m_filelist;
    }

    // used for decrypting CRCs
    static uint32 DecryptUInt32String(LPCTSTR szValue);

private:
    String              m_name;
    PackageManifest*    m_manifest;
    PackageFileEntry*   m_filelist;
};

#endif
